<!DOCTYPE html>












  


<html class="theme-next mist use-motion" lang="zh-Hans">
<head><meta name="generator" content="Hexo 3.9.0">
  <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">









  
  
    
  
  <link rel="stylesheet" media="all" href="https://cdnjs.cloudflare.com/ajax/libs/Han/3.3.0/han.min.css">




<meta http-equiv="Cache-Control" content="no-transform">
<meta http-equiv="Cache-Control" content="no-siteapp">



















  
  
  
  

  
    
    
  

  

  

  

  

  
    
    
    <link href="//fonts.googleapis.com/css?family=Lato:300,300italic,400,400italic,700,700italic&subset=latin,latin-ext" rel="stylesheet" type="text/css">
  






  

<link href="https://cdn.bootcss.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" type="text/css">

<link href="/css/main.css?v=6.3.0" rel="stylesheet" type="text/css">


  <link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png?v=6.3.0">


  <link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png?v=6.3.0">


  <link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png?v=6.3.0">


  <link rel="mask-icon" href="/images/logo.svg?v=6.3.0" color="#222">









<script type="text/javascript" id="hexo.configurations">
  var NexT = window.NexT || {};
  var CONFIG = {
    root: '/',
    scheme: 'Mist',
    version: '6.3.0',
    sidebar: {"position":"left","display":"hide","offset":12,"b2t":false,"scrollpercent":false,"onmobile":false},
    fancybox: false,
    fastclick: false,
    lazyload: false,
    tabs: true,
    motion: {"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},
    algolia: {
      applicationID: '',
      apiKey: '',
      indexName: '',
      hits: {"per_page":10},
      labels: {"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}
    }
  };
</script>


  




  <meta name="description" content="spring源码版本: 4.3.1-RELEASE  AbstractApplicationContext#refresh1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465public void refre">
<meta property="og:type" content="article">
<meta property="og:title" content="spring-refresh方法分析">
<meta property="og:url" content="http://codertang.com/2019/02/22/spring-refresh/index.html">
<meta property="og:site_name" content="Tonny&#39;s Blog">
<meta property="og:description" content="spring源码版本: 4.3.1-RELEASE  AbstractApplicationContext#refresh1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465public void refre">
<meta property="og:locale" content="zh-Hans">
<meta property="og:updated_time" content="2021-10-25T06:16:05.368Z">
<meta name="twitter:card" content="summary">
<meta name="twitter:title" content="spring-refresh方法分析">
<meta name="twitter:description" content="spring源码版本: 4.3.1-RELEASE  AbstractApplicationContext#refresh1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465public void refre">






  <link rel="canonical" href="http://codertang.com/2019/02/22/spring-refresh/">



<script type="text/javascript" id="page.configurations">
  CONFIG.page = {
    sidebar: "",
  };
</script>

  <title>spring-refresh方法分析 | Tonny's Blog</title>
  









  <noscript>
  <style type="text/css">
    .use-motion .motion-element,
    .use-motion .brand,
    .use-motion .menu-item,
    .sidebar-inner,
    .use-motion .post-block,
    .use-motion .pagination,
    .use-motion .comments,
    .use-motion .post-header,
    .use-motion .post-body,
    .use-motion .collection-title { opacity: initial; }

    .use-motion .logo,
    .use-motion .site-title,
    .use-motion .site-subtitle {
      opacity: initial;
      top: initial;
    }

    .use-motion {
      .logo-line-before i { left: initial; }
      .logo-line-after i { right: initial; }
    }
  </style>
</noscript>

</head>

<body itemscope itemtype="http://schema.org/WebPage" lang="zh-Hans">

  
  
    
  

  <div class="container sidebar-position-left page-post-detail">
    <div class="headband"></div>

    <header id="header" class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-wrapper">
  <div class="site-meta ">
    

    <div class="custom-logo-site-title">
      <a href="/" class="brand" rel="start">
        <span class="logo-line-before"><i></i></span>
        <span class="site-title">Tonny's Blog</span>
        <span class="logo-line-after"><i></i></span>
      </a>
    </div>
    
      
        <p class="site-subtitle">莫等闲, 白了少年头, 空悲切</p>
      
    
  </div>

  <div class="site-nav-toggle">
    <button aria-label="切换导航栏">
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
      <span class="btn-bar"></span>
    </button>
  </div>
</div>



<nav class="site-nav">
  
    <ul id="menu" class="menu">
      
        
        
        
          
          <li class="menu-item menu-item-home">
    <a href="/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-home"></i> <br>首页</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-tags">
    <a href="/tags/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-tags"></i> <br>标签</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-categories">
    <a href="/categories/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-th"></i> <br>分类</a>
  </li>
        
        
        
          
          <li class="menu-item menu-item-archives">
    <a href="/archives/" rel="section">
      <i class="menu-item-icon fa fa-fw fa-archive"></i> <br>归档</a>
  </li>

      
      
    </ul>
  

  
    

  

  
</nav>



  



</div>
    </header>

    


    <main id="main" class="main">
      <div class="main-inner">
        <div class="content-wrap">
          
          <div id="content" class="content">
            

  <div id="posts" class="posts-expand">
    

  

  
  
  

  

  <article class="post post-type-normal" itemscope itemtype="http://schema.org/Article">
  
  
  
  <div class="post-block">
    <link itemprop="mainEntityOfPage" href="http://codertang.com/2019/02/22/spring-refresh/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="name" content="Tonny Yi">
      <meta itemprop="description" content="Coding, Life, Zen">
      <meta itemprop="image" content="/images/avatar.png">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="Tonny's Blog">
    </span>

    
      <header class="post-header">

        
        
          <h1 class="post-title" itemprop="name headline">spring-refresh方法分析
              
            
          </h1>
        

        <div class="post-meta">
          <span class="post-time">

            
            
            

            
              <span class="post-meta-item-icon">
                <i class="fa fa-calendar-o"></i>
              </span>
              
                <span class="post-meta-item-text">发表于</span>
              

              
                
              

              <time title="创建时间：2019-02-22 14:58:15" itemprop="dateCreated datePublished" datetime="2019-02-22T14:58:15+08:00">2019-02-22</time>
            

            
              

              
                
                <span class="post-meta-divider">|</span>
                

                <span class="post-meta-item-icon">
                  <i class="fa fa-calendar-check-o"></i>
                </span>
                
                  <span class="post-meta-item-text">更新于</span>
                
                <time title="修改时间：2021-10-25 14:16:05" itemprop="dateModified" datetime="2021-10-25T14:16:05+08:00">2021-10-25</time>
              
            
          </span>

          

          
            
          

          
          

          

          

          

        </div>
      </header>
    

    
    
    
    <div class="post-body han-init-context" itemprop="articleBody">

      
      

      
        <blockquote>
<p>spring源码版本: 4.3.1-RELEASE</p>
</blockquote>
<p><code>AbstractApplicationContext#refresh</code><br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">refresh</span><span class="params">()</span> <span class="keyword">throws</span> BeansException, IllegalStateException </span>&#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (<span class="keyword">this</span>.startupShutdownMonitor) &#123;</span><br><span class="line">        <span class="comment">// 设置启动时间和启动标识active为true</span></span><br><span class="line">        <span class="comment">// 如果是web应用, 则会替换servletContextInitParams和servletConfigInitParams配置</span></span><br><span class="line">        prepareRefresh();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 创建DefaultListableBeanFactory, 加载BeanDefinition</span></span><br><span class="line">        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();</span><br><span class="line"></span><br><span class="line">        <span class="comment">// 设置BeanClassLoader, PropertyEditor注册器, 一些AwareProcessor</span></span><br><span class="line">        <span class="comment">// 把一些Aware接口排除出autowire</span></span><br><span class="line">        prepareBeanFactory(beanFactory);</span><br><span class="line"></span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            <span class="comment">// 给子类处理BeanFactory的机会, web容器时会增加一个BeanPostProcessor和排除一些Aware接口</span></span><br><span class="line">            postProcessBeanFactory(beanFactory);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 初始化并且执行已经注册的BeanFactoryPostProcessor</span></span><br><span class="line">            invokeBeanFactoryPostProcessors(beanFactory);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 初始化并执行已经注册的BeanPostProcessor</span></span><br><span class="line">            registerBeanPostProcessors(beanFactory);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 初始化上下文中的资源文件，如国际化文件的处理等</span></span><br><span class="line">            initMessageSource();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 初始化容器事件广播器</span></span><br><span class="line">            initApplicationEventMulticaster();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 给子类参与refresh的机会, web容器会在这里初始化主题资源</span></span><br><span class="line">            onRefresh();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 注册容器事件监听器</span></span><br><span class="line">            registerListeners();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 初始化剩余的非懒加载的单例bean, 开发人员定义的bean基本上是在这里实例化的</span></span><br><span class="line">            finishBeanFactoryInitialization(beanFactory);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 初始化LifecycleProcessor, 发布容器刷新事件</span></span><br><span class="line">            finishRefresh();</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">catch</span> (BeansException ex) &#123;</span><br><span class="line">            <span class="keyword">if</span> (logger.isWarnEnabled()) &#123;</span><br><span class="line">                logger.warn(<span class="string">"Exception encountered during context initialization - "</span> +</span><br><span class="line">                        <span class="string">"cancelling refresh attempt: "</span> + ex);</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 销毁所有已经创建的单例</span></span><br><span class="line">            destroyBeans();</span><br><span class="line"></span><br><span class="line">            <span class="comment">// 重置active标识</span></span><br><span class="line">            cancelRefresh(ex);</span><br><span class="line"></span><br><span class="line">            <span class="comment">// Propagate exception to caller.</span></span><br><span class="line">            <span class="keyword">throw</span> ex;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="keyword">finally</span> &#123;</span><br><span class="line">            <span class="comment">// Reset common introspection caches in Spring's core, since we</span></span><br><span class="line">            <span class="comment">// might not ever need metadata for singleton beans anymore...</span></span><br><span class="line">            resetCommonCaches();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<h2 id="prepareRefresh"><a href="#prepareRefresh" class="headerlink" title="prepareRefresh()"></a>prepareRefresh()</h2><p>主要是设置当前<code>ApplicationContext</code>的启动时间, <code>active</code>和<code>closed</code>标志位; 执行留给子类的扩展方法<code>initPropertySources()</code>; 验证系统必填属性(Spring默认没有配置必填属性)</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">prepareRefresh</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>.startupDate = System.currentTimeMillis();</span><br><span class="line">    <span class="keyword">this</span>.closed.set(<span class="keyword">false</span>);</span><br><span class="line">    <span class="keyword">this</span>.active.set(<span class="keyword">true</span>);</span><br><span class="line"></span><br><span class="line">    <span class="keyword">if</span> (logger.isInfoEnabled()) &#123;</span><br><span class="line">        logger.info(<span class="string">"Refreshing "</span> + <span class="keyword">this</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 留给子类初始化属性资源</span></span><br><span class="line">    initPropertySources();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 验证必填属性</span></span><br><span class="line">    getEnvironment().validateRequiredProperties();</span><br><span class="line"></span><br><span class="line">    <span class="comment">// Allow for the collection of early ApplicationEvents,</span></span><br><span class="line">    <span class="comment">// to be published once the multicaster is available...</span></span><br><span class="line">    <span class="keyword">this</span>.earlyApplicationEvents = <span class="keyword">new</span> LinkedHashSet&lt;ApplicationEvent&gt;();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<ul>
<li><p><strong><code>initPropertySources()</code></strong>在<code>AbstractApplicationContext</code>中实现是空的, 这是留给子类覆盖的, 这样就给了用户扩展的能力. 在web应用中, 子类<code>AbstractRefreshableWebApplicationContext</code>就利用覆盖整个方法将Servlet属性<code>ServletContext</code>和<code>servletConfig</code>注册到了系统属性资源中.</p>
</li>
<li><p><strong><code>getEnvironment().validateRequiredProperties()</code></strong>的实现是在<code>AbstractEnvironment</code>中, 整个方法用来验证一些必填属性, 而Spring默认是没有设置必填属性的, 所以这个方法的意义是什么呢? 扩展性! 如果我们自己有校验必填属性的需求, 比如我们要求应用启动时必须设置日志目录参数<code>logDir</code>, 就可以创建自己的ApplicationContext, 继承<code>XmlWebApplication</code>, 然后通过重写<code>initPropertySources()</code>方法设置该属性必填. </p>
  <figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">public</span> <span class="class"><span class="keyword">class</span> <span class="title">MyApplicationContext</span> <span class="keyword">extends</span> <span class="title">XmlWebApplicationContext</span> </span>&#123;</span><br><span class="line">    <span class="meta">@Override</span></span><br><span class="line">    <span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">initPropertySources</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.initPropertySources();</span><br><span class="line">        getEnvironment().setRequiredProperties(<span class="string">"logDir"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>  当我们运行项目时可以通过JVM参数<code>-DlogDir=/tmp/log</code>传递属性值, 如果不传递应用就会抛出<code>MissingRequiredPropertiesException</code>异常. 当然我们需要在<code>web.xml</code>中通过如下配置指定使用我们的自定义的<code>MyApplicationContext</code>.</p>
  <figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="tag">&lt;<span class="name">context-param</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">param-name</span>&gt;</span>contextClass<span class="tag">&lt;/<span class="name">param-name</span>&gt;</span></span><br><span class="line">    <span class="tag">&lt;<span class="name">param-value</span>&gt;</span>com.tonny.study.MyApplicationContext<span class="tag">&lt;/<span class="name">param-value</span>&gt;</span></span><br><span class="line"><span class="tag">&lt;/<span class="name">context-param</span>&gt;</span></span><br></pre></td></tr></table></figure>
</li>
</ul>
<h2 id="obtainFreshBeanFactory"><a href="#obtainFreshBeanFactory" class="headerlink" title="obtainFreshBeanFactory()"></a>obtainFreshBeanFactory()</h2><p>初始化BeanFactory, 加载BeanDefinition</p>
<figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> ConfigurableListableBeanFactory <span class="title">obtainFreshBeanFactory</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    refreshBeanFactory();</span><br><span class="line">    ConfigurableListableBeanFactory beanFactory = getBeanFactory();</span><br><span class="line">    <span class="keyword">if</span> (logger.isDebugEnabled()) &#123;</span><br><span class="line">        logger.debug(<span class="string">"Bean factory for "</span> + getDisplayName() + <span class="string">": "</span> + beanFactory);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> beanFactory;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>
<p>核心方法在<code>refreshBeanFactory()</code>, 它完成了创建BeanFactory和加载BeanDefinition<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// AbstractRefreshableApplicationContext#refreshBeanFactory</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">final</span> <span class="keyword">void</span> <span class="title">refreshBeanFactory</span><span class="params">()</span> <span class="keyword">throws</span> BeansException </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (hasBeanFactory()) &#123;</span><br><span class="line">        destroyBeans();</span><br><span class="line">        closeBeanFactory();</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">try</span> &#123;</span><br><span class="line">        DefaultListableBeanFactory beanFactory = createBeanFactory();</span><br><span class="line">        beanFactory.setSerializationId(getId());</span><br><span class="line">        customizeBeanFactory(beanFactory);</span><br><span class="line">        loadBeanDefinitions(beanFactory);</span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="keyword">this</span>.beanFactoryMonitor) &#123;</span><br><span class="line">            <span class="keyword">this</span>.beanFactory = beanFactory;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">catch</span> (IOException ex) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> ApplicationContextException(<span class="string">"I/O error parsing bean definition source for "</span> + getDisplayName(), ex);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>大概的逻辑如下:</p>
<ol>
<li>如果当前容器已经存在一个BeanFactory, 则将其销毁</li>
<li>通过<code>createBeanFactory()</code>创建一个BeanFactory实例, 具体创建的是<code>DefaultListableBeanFactory</code></li>
<li>自定义BeanFactory, spring的默认实现做了两件事: 是否允许覆盖同名称的不同定义的对象; 是否允许bean之间存在循环依赖. </li>
<li>加载BeanDefinition</li>
<li>将BeanFactory赋值给当前ApplicationContext</li>
</ol>
<p>简单说下第3步, 先看方法实现<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">customizeBeanFactory</span><span class="params">(DefaultListableBeanFactory beanFactory)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// 如果属性allowBeanDefinitionOverriding不为空，设置给beanFactory对象相应属性</span></span><br><span class="line">    <span class="comment">// 此属性的含义：是否允许覆盖同名称的不同定义的对象</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">this</span>.allowBeanDefinitionOverriding != <span class="keyword">null</span>) &#123;</span><br><span class="line">        beanFactory.setAllowBeanDefinitionOverriding(<span class="keyword">this</span>.allowBeanDefinitionOverriding);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">// 如果属性allowCircularReferences不为空，设置给beanFactory对象相应属性</span></span><br><span class="line">    <span class="comment">// 此属性的含义：是否允许bean之间存在循环依赖</span></span><br><span class="line">    <span class="keyword">if</span> (<span class="keyword">this</span>.allowCircularReferences != <span class="keyword">null</span>) &#123;</span><br><span class="line">        beanFactory.setAllowCircularReferences(<span class="keyword">this</span>.allowCircularReferences);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>Spring默认是没有对这两个属性进行设置的, 所以其实两个<code>if</code>判断都没有进, 那这两个属性有什么意义呢? 还是那句话, 扩展性, 子类可以通过覆盖<code>customizeBeanFactory()</code>修改这两个属性的配置.</p>
<p>下面说下第5步, 先看实现<br><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">loadBeanDefinitions</span><span class="params">(DefaultListableBeanFactory beanFactory)</span> <span class="keyword">throws</span> BeansException, IOException </span>&#123;</span><br><span class="line">    <span class="comment">// 创建XmlBeanDefinitionReader并交给BeanFactory</span></span><br><span class="line">    XmlBeanDefinitionReader beanDefinitionReader = <span class="keyword">new</span> XmlBeanDefinitionReader(beanFactory);</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 为beanDefinitionReader配置资源加载环境</span></span><br><span class="line">    beanDefinitionReader.setEnvironment(getEnvironment());</span><br><span class="line">    beanDefinitionReader.setResourceLoader(<span class="keyword">this</span>);</span><br><span class="line">    beanDefinitionReader.setEntityResolver(<span class="keyword">new</span> ResourceEntityResolver(<span class="keyword">this</span>));</span><br><span class="line"></span><br><span class="line">    <span class="comment">// 配置beanDefinitionReader内部的验证方式, 用于校验xml文件合法性(XSD/DTD)</span></span><br><span class="line">    initBeanDefinitionReader(beanDefinitionReader);</span><br><span class="line">    <span class="comment">// 解析xml文件, 生成并注册BeanDefinition</span></span><br><span class="line">    loadBeanDefinitions(beanDefinitionReader);</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 生成并注册BeanDefinition方法实现</span></span><br><span class="line"><span class="function"><span class="keyword">protected</span> <span class="keyword">void</span> <span class="title">loadBeanDefinitions</span><span class="params">(XmlBeanDefinitionReader reader)</span> <span class="keyword">throws</span> BeansException, IOException </span>&#123;</span><br><span class="line">    Resource[] configResources = getConfigResources();</span><br><span class="line">    <span class="keyword">if</span> (configResources != <span class="keyword">null</span>) &#123;</span><br><span class="line">        reader.loadBeanDefinitions(configResources);</span><br><span class="line">    &#125;</span><br><span class="line">    String[] configLocations = getConfigLocations();</span><br><span class="line">    <span class="keyword">if</span> (configLocations != <span class="keyword">null</span>) &#123;</span><br><span class="line">        reader.loadBeanDefinitions(configLocations);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></p>
<p>加载BeanDefinition时, 先通过<code>AbstractBeanDefinitionReader</code>的<code>loadBeanDefinitions</code>方法读取配置文件, 将其转换成<code>Resource</code>资源对象. 接着使用<code>XmlBeanDefinitionReader</code>的<code>doLoadBeanDefinitions</code>方法解析xml, 生成对应的<code>Document</code>对象. 然后遍历<code>Document</code>对象中的<code>Element</code>, 委托给<code>BeanDefinitionParserDelegate</code>解析, 主要包括: </p>
<ol>
<li>读取id/name/alias</li>
<li>校验name/alias的唯一性</li>
<li>读取class/parent属性 </li>
<li>调用<code>BeanDefinitionReaderUtils.createBeanDefinition</code>生成<code>GenericBeanDefinition</code>实例</li>
<li>解析并设置<code>singleton</code>/<code>lazy-init</code>/<code>autowire</code>/<code>depends-on</code>/<code>primary</code>/<code>init-method</code>/<code>destroy-method</code>/<code>Factory-bean</code>/<code>factory-method</code>等属性值</li>
<li>解析并设置构造函数参数配置</li>
<li>解析并设置实例属性(<code>property</code>)设置</li>
<li>创建并返回<code>BeanDefinitionHolder</code>, 将刚刚创建的<code>BeanDefinition</code>进行包装(<code>BeanDefinition</code>+<code>beanName</code>+<code>aliases</code>)</li>
</ol>
<p>最后使用<code>BeanDefinitionReaderUtils</code>调用<code>DefaultListableBeanFactory</code>的<code>registerBeanDefinition</code>方法, 使用<code>beanName</code>和<code>alias</code>将<code>BeanDefinition</code>进行注册</p>
<h2 id="prepareBeanFacotry-beanFactory"><a href="#prepareBeanFacotry-beanFactory" class="headerlink" title="prepareBeanFacotry(beanFactory)"></a>prepareBeanFacotry(beanFactory)</h2><ul>
<li><p>配置<code>classloader</code></p>
</li>
<li><p>配置SPEL表达式解析器(<code>#{...}</code>)<code>StandardBeanExpressionResolver</code></p>
</li>
<li><p>配置一个属性编辑器<code>ResourceEditorRegistrar</code>, 该类实现<code>PropertyEditor</code>, 此为java标准, 主要服务GUI, 实现String与Object互转.与之搭配的有<code>TypeConverter</code>接口进行统一封装, 3.0之后可能交由<code>ConversionService</code>处理</p>
<blockquote>
<p>从spring3.0开始, SPring重新设计了一套类型转换接口, 核心接口有: <code>Converter</code> <code>ConverterFactory</code> 和 <code>GenericConverter</code>. 在此基础上定义了<code>ConversionService</code>整合他们三个, 统一话接口操作, 可以称其为<strong>门面接口</strong>. 除此之外还有<code>Formatter&lt;T&gt;</code>接口能将Object转换成类型T也可以实现类型转换, 但它主要侧重与格式化, 一般用于时间/日期, 数字, 货币等场景.</p>
</blockquote>
</li>
<li><p>添加<code>ApplicationContextAware</code>接口后置处理器</p>
</li>
<li><p>配置几个忽略自动装配的<code>Aware</code>接口(<code>EnvironmentAware</code> <code>ResourceLoaderAware</code> <code>ApplicationEventPublisherAware</code> <code>MessageSourceAware</code> <code>ApplicationContextAware</code>)</p>
</li>
<li><p>配置几个自动装配的特殊类(<code>BeanFactory</code> <code>ResourceLoader</code> <code>ApplicationEventPublisher</code> <code>ApplicationContext</code>)</p>
</li>
<li><p>配置一个Bean后置处理器, 用来检测实现了<code>ApplicationListener</code>接口的类</p>
</li>
<li><p>注册单例(<code>Environment</code> <code>systemProperties</code> <code>systemEnvironment</code>)</p>
</li>
</ul>
<h2 id="postProcessBeanFactory-ConfigurableListableBeanFactory"><a href="#postProcessBeanFactory-ConfigurableListableBeanFactory" class="headerlink" title="postProcessBeanFactory(ConfigurableListableBeanFactory)"></a>postProcessBeanFactory(ConfigurableListableBeanFactory)</h2><p>该方法在<code>AbstractApplicationContext</code>中是空方法, 子类中<code>AbstractRefreshableWebApplicationContext</code>重写该方法:</p>
<ul>
<li>添加了<code>ServletContextAwareProcessor</code> </li>
<li>配置忽略自动注入接口<code>ServletContextAware</code>/<code>ServletConfigAware</code></li>
<li>注册单例对象<code>servletContext</code></li>
<li>注册环境对象属性<code>servletContext</code>和<code>servletConfig</code></li>
</ul>
<h2 id="invokeBeanFactoryPostProcessors-ConfigurableListableBeanFactory"><a href="#invokeBeanFactoryPostProcessors-ConfigurableListableBeanFactory" class="headerlink" title="invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory)"></a>invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory)</h2><p>调用<code>BeanFactoryPostProcessor</code></p>
<p>暂时为空</p>
<h2 id="registerBeanPostProcessors-ConfigurableListableBeanFactory"><a href="#registerBeanPostProcessors-ConfigurableListableBeanFactory" class="headerlink" title="registerBeanPostProcessors(ConfigurableListableBeanFactory)"></a>registerBeanPostProcessors(ConfigurableListableBeanFactory)</h2><p>从<code>BeanFactory</code>中获取<code>BeanPostProcessor</code>列表并调用</p>
<p>暂时为空</p>
<h2 id="initMessageSource"><a href="#initMessageSource" class="headerlink" title="initMessageSource"></a>initMessageSource</h2><p>消息提示内容国际化</p>
<h2 id="initApplicationEventMulticaster"><a href="#initApplicationEventMulticaster" class="headerlink" title="initApplicationEventMulticaster"></a>initApplicationEventMulticaster</h2><p>初始化应用事件广播器<code>SimpleApplicationEventMulticaster</code>, 并作为单例注册到<code>BeanFactory</code></p>
<h2 id="onRefresh"><a href="#onRefresh" class="headerlink" title="onRefresh"></a>onRefresh</h2><p>空, 留个容器子类扩展</p>
<h2 id="registerListeners"><a href="#registerListeners" class="headerlink" title="registerListeners"></a>registerListeners</h2><p>注册<code>ApplicationListener</code>实例</p>
<h2 id="finishBeanFactoryInitialization-ConfigurableListableBeanFactory"><a href="#finishBeanFactoryInitialization-ConfigurableListableBeanFactory" class="headerlink" title="finishBeanFactoryInitialization(ConfigurableListableBeanFactory)"></a>finishBeanFactoryInitialization(ConfigurableListableBeanFactory)</h2><p>实例化剩余单例对象</p>
<p>参考资料:<br><a href="http://cmsblogs.com/?p=4043" target="_blank" rel="noopener">【死磕 Spring】—– 深入分析 ApplicationContext 的 refresh()</a><br><a href="https://www.cnblogs.com/warehouse/p/9384735.html" target="_blank" rel="noopener">Spring源码分析（二十）准备环境</a><br><a href="https://www.cnblogs.com/warehouse/p/9385110.html" target="_blank" rel="noopener">Spring源码分析（二十一）加载BeanFactory</a></p>

      
    </div>

    

    
    
    

    

    

    
      <div>
        <ul class="post-copyright">
  <li class="post-copyright-author">
    <strong>本文作者： </strong>Tonny Yi</li>
  <li class="post-copyright-link">
    <strong>本文链接：</strong>
    <a href="http://codertang.com/2019/02/22/spring-refresh/" title="spring-refresh方法分析">http://codertang.com/2019/02/22/spring-refresh/</a>
  </li>
  <li class="post-copyright-license">
    <strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc/4.0/" rel="external nofollow" target="_blank">CC BY-NC 4.0</a> 许可协议。转载请注明出处！</li>
</ul>

      </div>
    

    <footer class="post-footer">
      

      
      
      

      
        <div class="post-nav">
          <div class="post-nav-next post-nav-item">
            
              <a href="/2019/02/21/hexo-drawing-echarts/" rel="next" title="在hexo中画图-ECharts">
                <i class="fa fa-chevron-left"></i> 在hexo中画图-ECharts
              </a>
            
          </div>

          <span class="post-nav-divider"></span>

          <div class="post-nav-prev post-nav-item">
            
              <a href="/2019/02/26/spring-resource/" rel="prev" title="spring-resource">
                spring-resource <i class="fa fa-chevron-right"></i>
              </a>
            
          </div>
        </div>
      

      
      
    </footer>
  </div>
  
  
  
  </article>



    <div class="post-spread">
      
    </div>
  </div>


          </div>
          

  



        </div>
        
          
  
  <div class="sidebar-toggle">
    <div class="sidebar-toggle-line-wrap">
      <span class="sidebar-toggle-line sidebar-toggle-line-first"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-middle"></span>
      <span class="sidebar-toggle-line sidebar-toggle-line-last"></span>
    </div>
  </div>

  <aside id="sidebar" class="sidebar">
    
    <div class="sidebar-inner">

      

      
        <ul class="sidebar-nav motion-element">
          <li class="sidebar-nav-toc sidebar-nav-active" data-target="post-toc-wrap">
            文章目录
          </li>
          <li class="sidebar-nav-overview" data-target="site-overview-wrap">
            站点概览
          </li>
        </ul>
      

      <section class="site-overview-wrap sidebar-panel">
        <div class="site-overview">
          <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
            
              <img class="site-author-image" itemprop="image" src="/images/avatar.png" alt="Tonny Yi">
            
              <p class="site-author-name" itemprop="name">Tonny Yi</p>
              <p class="site-description motion-element" itemprop="description">Coding, Life, Zen</p>
          </div>

          
            <nav class="site-state motion-element">
              
                <div class="site-state-item site-state-posts">
                
                  <a href="/archives/">
                
                    <span class="site-state-item-count">49</span>
                    <span class="site-state-item-name">日志</span>
                  </a>
                </div>
              

              
                
                
                <div class="site-state-item site-state-categories">
                  <a href="/categories/index.html">
                    
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                    <span class="site-state-item-count">42</span>
                    <span class="site-state-item-name">分类</span>
                  </a>
                </div>
              

              
                
                
                <div class="site-state-item site-state-tags">
                  <a href="/tags/index.html">
                    
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                      
                    
                    <span class="site-state-item-count">59</span>
                    <span class="site-state-item-name">标签</span>
                  </a>
                </div>
              
            </nav>
          

          

          
            <div class="links-of-author motion-element">
              
                <span class="links-of-author-item">
                  <a href="https://github.com/tonnyyi" target="_blank" title="GitHub"><i class="fa fa-fw fa-github"></i>GitHub</a>
                  
                </span>
              
                <span class="links-of-author-item">
                  <a href="mailto:tonnyemails@gmail.com" target="_blank" title="E-Mail"><i class="fa fa-fw fa-envelope"></i>E-Mail</a>
                  
                </span>
              
            </div>
          

          
          
            <div class="cc-license motion-element" itemprop="license">
              <a href="https://creativecommons.org/licenses/by-nc/4.0/" class="cc-opacity" target="_blank">
                <img src="/images/cc-by-nc.svg" alt="Creative Commons">
              </a>
            </div>
          

          
          

          
            
          
          

        </div>
      </section>

      
      <!--noindex-->
        <section class="post-toc-wrap motion-element sidebar-panel sidebar-panel-active">
          <div class="post-toc">

            
              
            

            
              <div class="post-toc-content"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#prepareRefresh"><span class="nav-number">1.</span> <span class="nav-text">prepareRefresh()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#obtainFreshBeanFactory"><span class="nav-number">2.</span> <span class="nav-text">obtainFreshBeanFactory()</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#prepareBeanFacotry-beanFactory"><span class="nav-number">3.</span> <span class="nav-text">prepareBeanFacotry(beanFactory)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#postProcessBeanFactory-ConfigurableListableBeanFactory"><span class="nav-number">4.</span> <span class="nav-text">postProcessBeanFactory(ConfigurableListableBeanFactory)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#invokeBeanFactoryPostProcessors-ConfigurableListableBeanFactory"><span class="nav-number">5.</span> <span class="nav-text">invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#registerBeanPostProcessors-ConfigurableListableBeanFactory"><span class="nav-number">6.</span> <span class="nav-text">registerBeanPostProcessors(ConfigurableListableBeanFactory)</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#initMessageSource"><span class="nav-number">7.</span> <span class="nav-text">initMessageSource</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#initApplicationEventMulticaster"><span class="nav-number">8.</span> <span class="nav-text">initApplicationEventMulticaster</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#onRefresh"><span class="nav-number">9.</span> <span class="nav-text">onRefresh</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#registerListeners"><span class="nav-number">10.</span> <span class="nav-text">registerListeners</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#finishBeanFactoryInitialization-ConfigurableListableBeanFactory"><span class="nav-number">11.</span> <span class="nav-text">finishBeanFactoryInitialization(ConfigurableListableBeanFactory)</span></a></li></ol></div>
            

          </div>
        </section>
      <!--/noindex-->
      

      

    </div>
  </aside>


        
      </div>
    </main>

    <footer id="footer" class="footer">
      <div class="footer-inner">
        <div class="copyright">&copy; <span itemprop="copyrightYear">2021</span>
  <span class="with-love" id="animate">
    <i class="fa fa-user"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">Tonny Yi</span>

  

  
</div>


  <script src="https://unpkg.com/mermaid@7.1.2/dist/mermaid.min.js"></script>
  <script>
    if (window.mermaid) {
      mermaid.initialize("");
    }
  </script>





  <div class="powered-by">由 <a class="theme-link" target="_blank" href="https://hexo.io">Hexo</a> 强力驱动</div>



  <span class="post-meta-divider">|</span>



  <div class="theme-info">主题 &mdash; <a class="theme-link" target="_blank" href="https://theme-next.org">NexT.Mist</a></div>




        








        
      </div>
    </footer>

    
      <div class="back-to-top">
        <i class="fa fa-arrow-up"></i>
        
      </div>
    

    

  </div>

  

<script type="text/javascript">
  if (Object.prototype.toString.call(window.Promise) !== '[object Function]') {
    window.Promise = null;
  }
</script>


























  
  
    <script type="text/javascript" src="/lib/jquery/index.js?v=2.1.3"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.min.js?v=1.2.1"></script>
  

  
  
    <script type="text/javascript" src="/lib/velocity/velocity.ui.min.js?v=1.2.1"></script>
  


  


  <script type="text/javascript" src="/js/src/utils.js?v=6.3.0"></script>

  <script type="text/javascript" src="/js/src/motion.js?v=6.3.0"></script>



  
  

  
  <script type="text/javascript" src="/js/src/scrollspy.js?v=6.3.0"></script>
<script type="text/javascript" src="/js/src/post-details.js?v=6.3.0"></script>



  


  <script type="text/javascript" src="/js/src/bootstrap.js?v=6.3.0"></script>



  



	





  





  










  





  

  

  

  

  
  

  

  

  

  

  
  <style>
    .copy-btn {
      display: inline-block;
      padding: 6px 12px;
      font-size: 13px;
      font-weight: 700;
      line-height: 20px;
      color: #333;
      white-space: nowrap;
      vertical-align: middle;
      cursor: pointer;
      background-color: #eee;
      background-image: linear-gradient(#fcfcfc, #eee);
      border: 1px solid #d5d5d5;
      border-radius: 3px;
      user-select: none;
      outline: 0;
    }

    .highlight-wrap .copy-btn {
      transition: opacity .3s ease-in-out;
      opacity: 0;
      padding: 2px 6px;
      position: absolute;
      right: 4px;
      top: 8px;
    }

    .highlight-wrap:hover .copy-btn,
    .highlight-wrap .copy-btn:focus {
      opacity: 1
    }

    .highlight-wrap {
      position: relative;
    }
  </style>
  <script>
    $('.highlight').each(function (i, e) {
      var $wrap = $('<div>').addClass('highlight-wrap')
      $(e).after($wrap)
      $wrap.append($('<button>').addClass('copy-btn').append('复制').on('click', function (e) {
        var code = $(this).parent().find('.code').find('.line').map(function (i, e) {
          return $(e).text()
        }).toArray().join('\n')
        var ta = document.createElement('textarea')
        document.body.appendChild(ta)
        ta.style.position = 'absolute'
        ta.style.top = '0px'
        ta.style.left = '0px'
        ta.value = code
        ta.select()
        ta.focus()
        var result = document.execCommand('copy')
        document.body.removeChild(ta)
        
          if(result)$(this).text('复制成功')
          else $(this).text('复制失败')
        
        $(this).blur()
      })).on('mouseleave', function (e) {
        var $b = $(this).find('.copy-btn')
        setTimeout(function () {
          $b.text('复制')
        }, 300)
      }).append(e)
    })
  </script>


</body>
</html>
